#region Copyright (C) 2007 Jens Winter

//Copyright (C) 2007 Jens Winter

//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.

//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#endregion

using System;
using System.Collections.Generic;
using System.Data;
using Castle.MicroKernel;
using Flynn.Core.ConnectionFactories;
using Flynn.Core.TaskResults;
using Flynn.Core.Tasks;
using MbUnit.Framework;
using Rhino.Mocks;

namespace Flynn.Core.Tests.Tasks
{
    [TestFixture]
    public class TaskExecutionServiceFixture
    {
        private MockRepository _mocks;

        private ITaskExecutionService _taskExecutionService;
        private IPrepareDatabaseTask _prepareDatabaseTask;
        private IUpdateDatabaseTask _updateDatabaseTask;
        private ICheckDatabaseTask _checkDatabaseTask;
        private IKernel _kernel;

        [SetUp]
        public void SetUp()
        {
            _mocks = new MockRepository();

            _prepareDatabaseTask = _mocks.CreateMock<IPrepareDatabaseTask>();
            _updateDatabaseTask = _mocks.CreateMock<IUpdateDatabaseTask>();
            _checkDatabaseTask = _mocks.CreateMock<ICheckDatabaseTask>();
            _kernel = new DefaultKernel();

            _taskExecutionService =
                new TaskExecutionService(_prepareDatabaseTask, _updateDatabaseTask, _checkDatabaseTask, _kernel);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void PrepareDatabaseTask_Must_Not_Be_Null()
        {
            new TaskExecutionService(null, _updateDatabaseTask, _checkDatabaseTask, _kernel);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void UpdateDatabaseTask_Must_Not_Be_Null()
        {
            new TaskExecutionService(_prepareDatabaseTask, null, _checkDatabaseTask, _kernel);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void CheckDatabaseTask_Must_Not_Be_Null()
        {
            new TaskExecutionService(_prepareDatabaseTask, _updateDatabaseTask, null, _kernel);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void Kernel_Must_Not_Be_Null()
        {
            new TaskExecutionService(_prepareDatabaseTask, _updateDatabaseTask, _checkDatabaseTask, null);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void When_Executing_PrepareDatabaseTask_Project_Must_Not_Be_Null()
        {
            _taskExecutionService.ExecutePrepareDatabaseTask(null, "ConnectionString");
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void When_Executing_UpdateDatabaseTask_Project_Must_Not_Be_Null()
        {
            _taskExecutionService.ExecuteUpdateDatabaseTask(null, "42", "ConnectionString");
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void When_Executing_CheckDatabaseTask_Project_Must_Not_Be_Null()
        {
            _taskExecutionService.ExecuteCheckDatabaseTask(null, "ConnectionString");
        }

        [Test]
        public void Can_Execute_PrepareDatabaseTask()
        {
            Project project = new Project { Database = "FakeDatabase" };

            IDbConnection connection = _mocks.CreateMock<IDbConnection>();
            IConnectionFactory connectionFactory = _mocks.CreateMock<IConnectionFactory>();

            _kernel.AddComponentInstance("ConnectionFactory.FakeDatabase", typeof(IConnectionFactory), connectionFactory);

            PreparingDatabaseResult expectedResult = new PreparingDatabaseResult();

            using (_mocks.Record())
            {
                using (_mocks.Ordered())
                {
                    Expect.Call(connectionFactory.CreateConnection("ConnectionString")).Return(connection);
                    connection.Open();
                    Expect.Call(_prepareDatabaseTask.PrepareDatabase(project, connection)).Return(expectedResult);
                    connection.Dispose();
                }
            }

            PreparingDatabaseResult actualResult;

            using (_mocks.Playback())
            {
                actualResult = _taskExecutionService.ExecutePrepareDatabaseTask(project, "ConnectionString");
            }

            Assert.AreSame(expectedResult, actualResult);
        }

        [Test]
        public void Report_Failure_When_Database_Is_Unknown_During_Preparing_Database()
        {
            Project project = new Project { Database = "UnknownDatabase" };

            PreparingDatabaseResult result = _taskExecutionService.ExecutePrepareDatabaseTask(project, "ConnectionString");

            Assert.IsTrue(result.Failed);
            Assert.AreEqual("Database 'UnknownDatabase' is unknown.", result.FailureMessage);
        }

        [Test]
        public void Report_Failure_When_Creating_Connection_Throws_Exception_During_Preparing_Database()
        {
            Project project = new Project { Database = "FakeDatabase" };

            IDbConnection connection = _mocks.CreateMock<IDbConnection>();
            IConnectionFactory connectionFactory = _mocks.CreateMock<IConnectionFactory>();

            _kernel.AddComponentInstance("ConnectionFactory.FakeDatabase", typeof(IConnectionFactory), connectionFactory);

            using (_mocks.Record())
            {
                using (_mocks.Ordered())
                {
                    Expect.Call(connectionFactory.CreateConnection("ConnectionString")).Throw(new Exception("Failure message"));
                }
            }

            using (_mocks.Playback())
            {
                PreparingDatabaseResult result = _taskExecutionService.ExecutePrepareDatabaseTask(project, "ConnectionString");

                Assert.IsTrue(result.Failed);
                Assert.AreEqual("Creating database connection failed (Failure message)", result.FailureMessage);
            }
        }

        [Test]
        public void Can_Execute_UpdateDatabaseTask()
        {
            Project project = new Project { Database = "FakeDatabase" };

            IDbConnection connection = _mocks.CreateMock<IDbConnection>();
            IConnectionFactory connectionFactory = _mocks.CreateMock<IConnectionFactory>();

            _kernel.AddComponentInstance("ConnectionFactory.FakeDatabase", typeof(IConnectionFactory), connectionFactory);

            UpdatingDatabaseResult expectedResult = new UpdatingDatabaseResult();

            using (_mocks.Record())
            {
                using (_mocks.Ordered())
                {
                    Expect.Call(connectionFactory.CreateConnection("ConnectionString")).Return(connection);
                    connection.Open();
                    Expect.Call(_updateDatabaseTask.UpdateDatabase(project, "42", connection)).Return(expectedResult);
                    connection.Dispose();
                }
            }

            UpdatingDatabaseResult actualResult;

            using (_mocks.Playback())
            {
                actualResult = _taskExecutionService.ExecuteUpdateDatabaseTask(project, "42", "ConnectionString");
            }

            Assert.AreSame(expectedResult, actualResult);
        }

        [Test]
        public void Report_Failure_When_Database_Is_Unknown_During_Updating_Database()
        {
            Project project = new Project { Database = "UnknownDatabase" };

            UpdatingDatabaseResult result = _taskExecutionService.ExecuteUpdateDatabaseTask(project, "42", "ConnectionString");

            Assert.IsTrue(result.Failed);
            Assert.AreEqual("Database 'UnknownDatabase' is unknown.", result.FailureMessage);
        }

        [Test]
        public void Report_Failure_When_Creating_Connection_Throws_Exception_During_Updating_Database()
        {
            Project project = new Project { Database = "FakeDatabase" };

            IDbConnection connection = _mocks.CreateMock<IDbConnection>();
            IConnectionFactory connectionFactory = _mocks.CreateMock<IConnectionFactory>();

            _kernel.AddComponentInstance("ConnectionFactory.FakeDatabase", typeof(IConnectionFactory), connectionFactory);

            using (_mocks.Record())
            {
                using (_mocks.Ordered())
                {
                    Expect.Call(connectionFactory.CreateConnection("ConnectionString")).Throw(new Exception("Failure message"));
                }
            }

            using (_mocks.Playback())
            {
                UpdatingDatabaseResult result = _taskExecutionService.ExecuteUpdateDatabaseTask(project, "42", "ConnectionString");

                Assert.IsTrue(result.Failed);
                Assert.AreEqual("Creating database connection failed (Failure message)", result.FailureMessage);
            }
        }

        [Test]
        public void Can_Execute_CheckDatabaseTask()
        {
            Project project = new Project { Database = "FakeDatabase" };

            IDbConnection connection = _mocks.CreateMock<IDbConnection>();
            IConnectionFactory connectionFactory = _mocks.CreateMock<IConnectionFactory>();

            _kernel.AddComponentInstance("ConnectionFactory.FakeDatabase", typeof(IConnectionFactory), connectionFactory);

            CheckingDatabaseResult expectedResult = CheckingDatabaseResult.Success(string.Empty);

            using (_mocks.Record())
            {
                using (_mocks.Ordered())
                {
                    Expect.Call(connectionFactory.CreateConnection("ConnectionString")).Return(connection);
                    connection.Open();
                    Expect.Call(_checkDatabaseTask.CheckDatabase(project, connection)).Return(expectedResult);
                    connection.Dispose();
                }
            }

            CheckingDatabaseResult actualResult;

            using (_mocks.Playback())
            {
                actualResult = _taskExecutionService.ExecuteCheckDatabaseTask(project, "ConnectionString");
            }

            Assert.AreSame(expectedResult, actualResult);
        }

        [Test]
        public void Report_Failure_When_Database_Is_Unknown_During_Checking_Database()
        {
            Project project = new Project { Database = "UnknownDatabase" };

            CheckingDatabaseResult result = _taskExecutionService.ExecuteCheckDatabaseTask(project, "ConnectionString");

            Assert.IsTrue(result.Failed);
            Assert.AreEqual("Database 'UnknownDatabase' is unknown.", result.FailureMessage);
        }

        [Test]
        public void Report_Failure_When_Creating_Connection_Throws_Exception_During_Checking_Database()
        {
            Project project = new Project { Database = "FakeDatabase" };

            IConnectionFactory connectionFactory = _mocks.CreateMock<IConnectionFactory>();

            _kernel.AddComponentInstance("ConnectionFactory.FakeDatabase", typeof(IConnectionFactory), connectionFactory);

            using (_mocks.Record())
            {
                using (_mocks.Ordered())
                {
                    Expect.Call(connectionFactory.CreateConnection("ConnectionString")).Throw(new Exception("Failure message"));
                }
            }

            using (_mocks.Playback())
            {
                CheckingDatabaseResult result = _taskExecutionService.ExecuteCheckDatabaseTask(project, "ConnectionString");

                Assert.IsTrue(result.Failed);
                Assert.AreEqual("Creating database connection failed (Failure message)", result.FailureMessage);
            }
        }
    }
}